package com.codefalse.mybatis.gen;

import com.codefalse.mybatis.gen.model.TableModel;
import freemarker.template.Template;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.ProgressCallback;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Mojo(name = "generate")
public class MybatisGenMojo extends AbstractMojo implements ProgressCallback {
    private static final List<String> warnings = new ArrayList<>();

    @Parameter(name = "host", required = true)
    private String host;
    @Parameter(name = "port", defaultValue = "3306")
    private Integer port;
    @Parameter(name = "database", required = true)
    private String database;
    @Parameter(name = "user")
    private String user;
    @Parameter(name = "password")
    private String password;
    @Parameter(name = "tableName", required = true)
    private String tableName;

    @Parameter(name = "targetRuntime", defaultValue = "MyBatis3")
    private String targetRuntime;
    @Parameter(name = "baseDir", defaultValue = "${basedir}")
    private String baseDir;
    @Parameter(name = "sourceDirectory", defaultValue = "src/main/java")
    private String sourceDirectory;
    @Parameter(name = "resourcesDirectory", defaultValue = "src/main/resources")
    private String resourcesDirectory;
    @Parameter(name = "modelPackage", defaultValue = "${project.groupId}.entity.gen")
    private String modelPackage;
    @Parameter(name = "mapperPackage", defaultValue = "${project.groupId}.mapper.gen")
    private String mapperPackage;
    @Parameter(name = "xmlDirectory", defaultValue = "mapper.gen")
    private String xmlDirectory;

    @Parameter(name = "pluginLombok", defaultValue = "true")
    private Boolean pluginLombok;
    @Parameter(name = "pluginModelColumn", defaultValue = "true")
    private Boolean pluginModelColumn;
    @Parameter(name = "pluginDynamicWhere", defaultValue = "true")
    private Boolean pluginDynamicWhere;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        warnings.clear();

        String connUrl = "jdbc:mysql://" + host + ":" + port + "/" + database;

        JDBCConnectionConfiguration connConfig = new JDBCConnectionConfiguration();
        connConfig.setDriverClass("com.mysql.jdbc.Driver");
        connConfig.setConnectionURL(connUrl);
        connConfig.setUserId(user);
        connConfig.setPassword(password);

        if (!sourceDirectory.startsWith("/")) {
            sourceDirectory = "/" + sourceDirectory;
        }
        String projectSourcePath = baseDir + sourceDirectory;
        JavaModelGeneratorConfiguration modelConfig = new JavaModelGeneratorConfiguration();
        modelConfig.setTargetProject(projectSourcePath);
        modelConfig.setTargetPackage(modelPackage);

        JavaClientGeneratorConfiguration clientConfig = new JavaClientGeneratorConfiguration();
        clientConfig.setConfigurationType("XMLMAPPER");
        clientConfig.setTargetProject(projectSourcePath);
        clientConfig.setTargetPackage(mapperPackage);

        if (!resourcesDirectory.startsWith("/")) {
            resourcesDirectory = "/" + resourcesDirectory;
        }
        String projectResourcesPath = baseDir + resourcesDirectory;
        SqlMapGeneratorConfiguration xmlConfig = new SqlMapGeneratorConfiguration();
        xmlConfig.setTargetProject(projectResourcesPath);
        xmlConfig.setTargetPackage(xmlDirectory);

        CommentGeneratorConfiguration commentConfig = new CommentGeneratorConfiguration();

        List<TableModel> tableModels = GenerateFactory.buildTables(database, tableName, connUrl, user, password);

        Configuration configuration = GenerateFactory.getConfiguration(targetRuntime,
                connConfig, modelConfig, clientConfig, xmlConfig, commentConfig, tableModels,
                pluginLombok, pluginModelColumn, pluginDynamicWhere);
        DefaultShellCallback shellCallback = new DefaultShellCallback(true);
        try {
            MyBatisGenerator generator = new MyBatisGenerator(configuration, shellCallback, warnings);
            generator.generate(this);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void generateExtFile() throws Exception{
        if (!sourceDirectory.startsWith("/")) {
            sourceDirectory = "/" + sourceDirectory;
        }
        String projectSourcePath = baseDir + sourceDirectory;
        String projectSourceModelPath = projectSourcePath + "\\" + modelPackage.replaceAll("\\.", "\\");
        String projectSourceClientPath = projectSourcePath + "\\" + mapperPackage.replaceAll("\\.", "\\");

        if (!resourcesDirectory.startsWith("/")) {
            resourcesDirectory = "/" + resourcesDirectory;
        }
        String projectResourcesPath = baseDir + resourcesDirectory;
        String projectXmlPath = projectResourcesPath + "\\" + xmlDirectory.replaceAll("\\.", "\\");

        freemarker.template.Configuration configuration = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_31);
        configuration.setClassLoaderForTemplateLoading(this.getClass().getClassLoader(), "freemarker");
        configuration.setDefaultEncoding("UTF-8");

        Map<String, Object> map = new HashMap<>();
        map.put("modelPackage", modelPackage);
        map.put("mapperPackage", mapperPackage);

        Template whereItemTemplate = configuration.getTemplate("whereItem.ftl");
        String whereItemPath = projectSourceModelPath + "\\" + "WhereItem.java";
        whereItemTemplate.process(map, new OutputStreamWriter(new FileOutputStream(whereItemPath), StandardCharsets.UTF_8));
        getLog().info("生成：" + whereItemPath);

        Template whereDynamicTemplate = configuration.getTemplate("WhereDynamic.ftl");
        String whereDynamicPath = projectSourceModelPath + "\\" + "WhereDynamic.java";
        whereDynamicTemplate.process(map, new OutputStreamWriter(new FileOutputStream(whereDynamicPath), StandardCharsets.UTF_8));
        getLog().info("生成：" + whereDynamicPath);

        Template whereFactoryTemplate = configuration.getTemplate("WhereFactory.ftl");
        String whereFactoryPath = projectSourceModelPath + "\\" + "WhereFactory.java";
        whereFactoryTemplate.process(map, new OutputStreamWriter(new FileOutputStream(whereFactoryPath), StandardCharsets.UTF_8));
        getLog().info("生成：" + whereFactoryPath);

        Template commonMapperTemplate = configuration.getTemplate("CommonMapper.ftl");
        String commonMapperPath = projectSourceClientPath + "\\" + "CommonMapper.java";
        commonMapperTemplate.process(map, new OutputStreamWriter(new FileOutputStream(commonMapperPath), StandardCharsets.UTF_8));
        getLog().info("生成：" + commonMapperPath);

        Template commonXMLTemplate = configuration.getTemplate("CommonXML.ftl");
        String commonXMLPath = projectXmlPath + "\\" + "CommonMapper.xml";
        commonXMLTemplate.process(map, new OutputStreamWriter(new FileOutputStream(commonXMLPath), StandardCharsets.UTF_8));
        getLog().info("生成：" + commonXMLPath);
    }

    @Override
    public void introspectionStarted(int i) {
        getLog().info("=======================执行开始=========================");
    }

    @Override
    public void generationStarted(int i) {

    }

    @Override
    public void saveStarted(int i) {
        getLog().info("Mybatis Generator本次生成" + i + "个文件");
        try {
            this.generateExtFile();
        } catch (Exception e) {
            getLog().error("生成Mybatis扩展失败");
            e.printStackTrace();
        }
    }

    @Override
    public void startTask(String s) {
        getLog().info("开始任务：" + s);
    }

    @Override
    public void done() {
        for (String warn : warnings) {
            getLog().warn(warn);
        }
        getLog().info("=======================执行完成=========================");
    }

    @Override
    public void checkCancel() throws InterruptedException {

    }
}
